{%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
{%macro NestedObjectName(name) -%}
    {{className}}::{{-name | UpperCamelCase-}}
{%-endmacro%}
{%-macro ObjectType(propName, propSchema) -%}
    {%-if '$ref' in propSchema -%}
        {{-loader.Reference(resolver, propSchema['$ref'])-}}
    {%-else-%}
        {{-NestedObjectName(propName)-}}
    {%-endif-%}
{%-endmacro%}
{%-macro ObjectName(propName, propSchema) -%}
    {%-if '$ref' in propSchema -%}
        {{-resolver.cpp_get_name(propSchema['$ref'])|UpperCamelCase-}}
    {%-else-%}
        {{-propName | UpperCamelCase-}}
    {%-endif-%}
{%-endmacro%}
{%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}

{%import 'loader.jinja2' as loader with context%}
{%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
{{loader.Class('cpp', resolver, [className], componentName, s) }}
{%-endfor%}


{{className}}::{{Name}}()
{

}

{%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
boost::optional<{{ObjectType(componentName, s)}}> {{className}}::Get{{ObjectName(componentName, s)}}() const
{
    return {{componentName|privatize}};
}

void {{className}}::Set{{ObjectName(componentName, s)}}(const {{ObjectType(componentName, s)}}& component)
{
    {{componentName|privatize}} = component;
    {{componentName|privatize}}->SetHandle(_handle);
}
{%endfor%}

{{className}} {{className}}::FromJson(const rapidjson::Value& json)
{
    {%-if schema.requiredProperties | length > 0 %}
    ThrowIfMissingRequiredProperties(json);
    {%-endif%}
    {{className}} returnObject;
    {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
    try
    {
        returnObject.Set{{ObjectName(componentName, s)}}({{ObjectType(componentName, s)}}::FromJson(json));
    }
    catch (...)
    {
        // If the type didn't parse, then no big deal since AnyOf doesn't require it to
    }
    {%-endfor%}

    return returnObject;
}

void {{className}}::ToJson(rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator) const
{
    if (!value.IsObject())
    {
        value.SetObject();
    }

    {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
    if ({{componentName|privatize}})
    {
        {{componentName|privatize}}->ToJson(value, allocator);
    }
    {%-endfor %}
    {%-if schema.requiredProperties | length > 0 %}
    ThrowIfMissingRequiredProperties(value);
    {%-endif%}
}

void {{className}}::SetHandle(const std::string& handle)
{
    _handle = handle;

    // This 'anyOf' object passes the handle through to its children objects
    {%-for s in schema.anyOf %}{%set componentName%}Component{{loop.index}}{%endset%}
    if ({{componentName|privatize}})
    {
        {{componentName|privatize}}->SetHandle(handle);
    }
    {%-endfor %}
}

std::string {{className}}::GetHandle() const
{
    return _handle;
}

{%if schema.requiredProperties | length > 0 %}
void {{className}}::ThrowIfMissingRequiredProperties(const rapidjson::Value& json)
{
    if (!json.IsObject())
    {
        throw {{exception}}("Not an object");
    }
    {%-for reqProp in schema.requiredProperties |sort %}
    if (!json.HasMember("{{reqProp}}"))
    {
        throw {{exception}}("Missing '{{reqProp}}' property");
    }
    {%-endfor%}
}
{%-endif%}
